<template>
  <div>
    <div>
      <el-input
        placeholder="请输入分类名称"
        v-model="filterText"
        clearable
      ></el-input>
    </div>
    <br />
    <!-- 拖拽开关 -->
    <div>
      <el-switch
        style="display: block"
        v-model="draggable"
        active-color="#13ce66"
        inactive-color="#ff4949"
        active-text="开启拖拽"
        inactive-text="关闭拖拽"
      >
      </el-switch>
    </div>
    <br />

    <!-- 树形控件 -->
    <div>
      <el-tree
        :data="categoryList"
        :props="defaultProps"
        :allow-drop="allowDrop"
        :expand-on-click-node="false"
        :default-expanded-keys="expandedKeys"
        :filter-node-method="filterNode"
        :draggable="draggable"
        show-checkbox
        node-key="catId"
        @node-drop="nodeDrop"
        @node-click="handleNodeClick"
        ref="tree"
      >
        <!-- 添加、修改、删除按钮 -->
        <span class="custom-tree-node" slot-scope="{ node, data }">
          <span>{{ node.label }}</span>
          <span>
            <el-button
              v-if="data.catLevel != 3"
              type="text"
              size="mini"
              @click="() => append(data)"
              >Append
            </el-button>
            <el-button type="text" size="mini" @click="() => update(data)">
              Update
            </el-button>
            <el-button
              v-if="node.childNodes.length == 0"
              type="text"
              size="mini"
              @click="() => remove(node, data)"
              >Delete
            </el-button>
          </span>
        </span>
      </el-tree>
    </div>

    <!-- 分类的添加/修改 -->
    <el-dialog
      :title="!this.categoryForm.catId ? '添加分类' : '修改分类信息'"
      :close-on-click-modal="false"
      :visible.sync="categoryDialogFormVisible"
    >
      <el-form :model="categoryForm">
        <el-form-item label="分类名称" label-width="120px">
          <el-input v-model="categoryForm.name" autocomplete="off"></el-input>
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button @click="categoryDialogFormVisible = false">取 消</el-button>
        <el-button type="primary" @click="save()">确 定</el-button>
      </div>
    </el-dialog>
  </div>
</template>

<script>
//这里可以导入其他文件（比如:组件,工具js,第三方插件jsjson文件,图片文件等等)
//例如:import《组件名称》from '《组件路径》';

export default {
  //import引入的组件需要注入到对象中才能使用
  components: {},
  props: {},
  data() {
    //这里存放数据
    return {
      // 拖拽功能 默认为关闭状态
      draggable: false,
      // 用于存放最后传入数据库的数据节点信息
      updateNodes: [],
      // 用于计算拖拽的层级
      countLevel: 1,
      // 分类的模糊查找
      filterText: "",
      // 默认展开的节点
      expandedKeys: [],
      // 分类的添加修改 对话框
      categoryDialogFormVisible: false,
      // categoryForm存放 分类的节点信息
      categoryForm: {
        catId: 0,
        catLevel: 0,
        children: null,
        name: null,
        icon: null,
        parentCid: 0,
        productCount: 0,
        productUnit: null,
        showStatus: 1,
        sort: 0,
      },

      // 存放分类列表
      categoryList: [],
      defaultProps: {
        children: "children",
        label: "name",
      },
    };
  },
  //计算属性 类型于data概念
  computed: {},
  //监控data中的数据变化
  watch: {
    filterText(val) {
      this.$refs.tree.filter(val);
    },
  },
  //方法集合
  methods: {
    /*********************************拖拽成功触发的事件
     * 被拖拽节点对应的 Node、
     * 结束拖拽时最后进入的节点、
     * 被拖拽节点的放置位置（before、inner、after）
     * 1、需要传什么对象到后台
     * 1.1、拖拽成功后哪些字段需要发生改变
     * parentCid
     * sort
     * catLevel
     */
    nodeDrop(draggingNode, dropNode, type) {
      this.updateNodes = []; // 对拖拽后需要进行修改的集合初始化
      //console.log("拖拽成功触发的事件：", draggingNode, dropNode, type);
      // 当前节点最新的父节点
      let parentCid = 0; // 拖拽节点的父级id 默认设置为0
      let siblings = null; // 需要进行排序的结点集合
      if (type == "before" || type == "after") {
        // 如果只是相同层级进行拖动，那么当前节点的父id就是目标对象的parentCid
        parentCid = dropNode.data.parentCid;

        // 同级拖动 --> 需要重新排序的节点集合 = 目标对象的父节点的子元素集合
        siblings = dropNode.parent.childNodes;
      } else {
        // 如果拖动类型为inner，不是相同层级拖动，那么当前节点的父级id就是目标对象的catId
        parentCid = dropNode.data.catId;

        // 拖动到节点内部 --> 需要重新排序的节点集合 = 目标对象的子元素集合
        siblings = dropNode.childNodes;
      }

      //console.log("需要进行排序的节点集合：", siblings);
      // 对当前拖拽需要排序的节点集合进行排序
      for (let i = 0; i < siblings.length; i++) {
        // 如果需要排序的节点和拖拽的节点相同，说明改变了父节点，需要将parentCid重新赋值
        if (siblings[i].data.catId == draggingNode.data.catId) {
          let catLevel = draggingNode.level; // 默认为被拖拽节点的层级
          // 判断遍历到的节点的层级和默认节点的层级是否一致，如果不一致就将遍历到的节点层级赋值给默认节点层级
          if (siblings[i].level != catLevel) {
            //console.log("节点不一致：", siblings[i]);
            catLevel = siblings[i].level;
            //调用进行当前节点的子元素节点层级的修改
            this.updateChildNodeLevel(siblings[i]);
          }
          //
          //
          this.updateNodes.push({
            catId: siblings[i].data.catId,
            sort: i,
            parentCid,
            catLevel,
          });
        } else {
          this.updateNodes.push({ catId: siblings[i].data.catId, sort: i });
        }
      }

      //console.log("传入后端的节点集合：", this.updateNodes);

      // 当前拖拽节点的最新层级

      // 发送请求修改数据
      this.$confirm(`此操作无法撤销, 是否继续?`, "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning",
      })
        .then(() => {
          this.$http({
            url: this.$http.adornUrl("/product/category/update/sort"),
            method: "post",
            data: this.$http.adornData(this.updateNodes, false),
          }).then(({ data }) => {
            this.$message({
              type: "success",
              message: "该分类移动成功!",
            });
            //设置默认展开的分类
            this.expandedKeys = [parentCid];
            // 刷新分类列表
            this.getCategoryList();
          });
        })
        .catch(() => {
          this.$message({
            type: "info",
            message: "分类移动失败",
          });
          //设置默认展开的分类
          this.expandedKeys = [parentCid];
          // 刷新分类列表
          this.getCategoryList();
        });
    },
    // 修改子节点层级
    updateChildNodeLevel(node) {
      //console.log("要修改的子节点：", node);

      // 判断子节点的长度是否大于0，如果大于0，说明可以遍历子节点
      if (node.childNodes.length > 0) {
        // 遍历需要修改的子节点集合
        for (let i in node.childNodes) {
          // 获取子节点的数据
          var cNode = node.childNodes[i].data;
          // 重新赋值给updateNodes
          this.updateNodes.push({
            catId: cNode.catId,
            catLevel: node.childNodes[i].level,
          });
          // 继续修改他的子节点的层级
          this.updateChildNodeLevel(node.childNodes[i]);
        }
      }
    },
    /********************************* 判断拖拽节点是否可以进行拖拽操作
     * draggingNode被拖拽对象
     * dropNode目标放置对象
     * type 拖拽类型（prev，inner，next）
     * 1、判断目标对象中能否放置被拖拽的对象
     * 1.1、计算被拖拽的层级数量
     * 1.2、获取到层级和目标对象的层级是否大于3
     */
    allowDrop(draggingNode, dropNode, type) {
      //console.log(draggingNode, dropNode, type);
      this.countNodeLevel(draggingNode);
      //console.log("被拖拽的层级数：", this.countLevel);

      //根据计算好的层级数判断拖拽目标的层级数是否大于3
      // 判断拖拽类型为前后的情况是否可拖拽
      if (type == "next" || type == "prev") {
        // 当被拖拽对象层级为1时，目标对象的层级为任何层级（1/2/3）都可进行拖拽
        if (this.countLevel == 1) {
          return true;
        }
        // 当被拖拽对象层级为2时，目标对象的层级只要不是3级即可进行拖拽
        if (this.countLevel == 2 && dropNode.level != 3) {
          return true;
        }
        // 当被拖拽对象层级为3时，目标对象的层级只能是1级的时候可进行拖拽
        if (this.countLevel == 3 && dropNode.level == 1) {
          return true;
        }
      } else if (type == "inner") {
        // 当被拖拽对象层级为1时，目标对象的层级只要不是3级即可进行拖拽
        if (this.countLevel == 1 && dropNode.level != 3) {
          return true;
        }
        // 当被拖拽对象层级为2时，目标对象的层级只能是1级的时候可进行拖拽
        if (this.countLevel == 2 && dropNode.level == 1) {
          return true;
        }
        // 当被拖拽对象层级为3时，只能拖拽到同级的对象中，现在判断的是拖拽类型为内部，所以不能进行拖拽
      }
      return false;
    },
    // 计算拖拽对象层级的方法
    countNodeLevel(node) {
      //console.log(node);
      // 初始化计算层级数量
      this.countLevel = 1;
      // 判断层级中是否有子节点
      if (node.childNodes.length > 0) {
        this.countLevel++;
        for (let i in node.childNodes) {
          if (node.childNodes[i].childNodes.length > 0) {
            this.countLevel++;
            return; //最高为3层，如果判断到有3层的情况的时候就结束循环,因为我们做的是三级分类，不能出现四级以上的情况
          }
        }
      }
    },

    // 表单的初始化
    initCategoryForm() {
      // 将其他的属性初始化
      this.categoryForm.catId = null;
      this.categoryForm.catLevel = 0;
      this.categoryForm.children = null;
      this.categoryForm.name = null;
      this.categoryForm.icon = null;
      this.categoryForm.parentCid = 0;
      this.categoryForm.productCount = 0;
      this.categoryForm.productUnit = null;
      this.categoryForm.showStatus = 1; // 默认为1 展示状态
      this.categoryForm.sort = 0; // 排序属性 默认为0
    },

    // 分类的模糊查找
    filterNode(value, data) {
      if (!value) return true;
      return data.name.indexOf(value) !== -1;
    },

    // 分类的删除
    remove(node, data) {
      var ids = [data.catId];
      this.$confirm(`此操作会将分类：【${data.name}】删除, 是否继续?`, "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning",
      })
        .then(() => {
          this.$http({
            url: this.$http.adornUrl("/product/category/delete"),
            method: "post",
            data: this.$http.adornData(ids, false),
          }).then(({ data }) => {
            //设置默认展开的分类，将删除节点的父节点的catId赋值给expandedKeys
            this.expandedKeys = [node.parent.data.catId];
            // 刷新分类列表
            this.getCategoryList();
            this.$message({
              type: "success",
              message: "该分类删除成功!",
            });
          });
        })
        .catch(() => {
          this.$message({
            type: "info",
            message: "已取消删除该分类",
          });
        });
    },

    // 分类的修改按钮
    update(data) {
      //console.log("点击修改按钮获取到的data:", data);
      //查询该分类
      this.$http({
        url: this.$http.adornUrl(`/product/category/info/${data.catId}`),
        method: "get",
      }).then(({ data }) => {
        this.categoryForm = data.category;
        this.categoryDialogFormVisible = true;
      });
    },

    // 分类添加按钮
    append(data) {
      //console.log("点击添加按钮获取到的data:", data);
      // 打开添加/修改对话框
      this.categoryDialogFormVisible = true;
      // 调用初始化表单方法对表单进行初始化，防止有点击修改按钮后对表单进行赋值没有清空的情况
      this.initCategoryForm();
      // 添加的父id= 上一级分类的catid
      this.categoryForm.parentCid = data.catId;
      // 所添加节点的层级 = 上一级层级 + 1
      this.categoryForm.catLevel = data.catLevel + 1;
    },
    // 确认保存按钮
    /**
     * 使用已知求未知
     * 已知条件
     * catId
     * catLevel
     *
     * 未知条件
     * categoryForm.parentCid = data.catId
     * categoryForm.catLevel = data.catLevel + 1
     *
     */
    save() {
      this.$http({
        url: this.$http.adornUrl(
          `/product/category/${!this.categoryForm.catId ? "save" : "update"}`
        ),
        method: "post",
        // false参数是为了不将t参数拼接到参数列表中
        data: this.$http.adornData(this.categoryForm, false),
      }).then(({ data }) => {
        if (data && data.code === 0) {
          this.$message({
            message: "保存成功",
            type: "success",
          });
          // 关闭弹窗，查询列表
          this.categoryDialogFormVisible = false;
          this.getCategoryList();
          // 将新保存的分类的父id保存到默认展开的数组中
          this.expandedKeys = [this.categoryForm.parentCid];
        } else {
          this.$message.error(data.msg);
        }
      });
    },

    // 获取分类列表
    getCategoryList() {
      this.$http({
        url: this.$http.adornUrl("/product/category/list/tree"),
        method: "get",
      }).then(({ data }) => {
        //console.log(data);
        this.categoryList = data.data;
      });
    },
    handleNodeClick(data) {
      console.log("点击分类时触发的事件：",data);
    },
  },
  //生命周期 -创建完成*(可以访问当前this实例)
  created() {
    this.getCategoryList();
  },
  //生命周期 -挂载完成(可以访问dom元素)
  mounted() {},
  beforeCreate() {}, //生命周期–创建之前
  beforeMount() {}, //生命周期–挂载之前
  beforeUpdate() {}, //生命周期–更新之前
  updated() {}, //生命周期–更新之后
  beforeDestroy() {}, //生命周期–销毁之前
  destroyed() {}, //生命周期–销毁完成
  activated() {}, //如果页面有keep-alive缓存功能,这个函数会触发
};
</script>
<style scoped>
</style>